using System;

namespace RapidHDL
{
    /// <summary>
    /// Summary description for Register.
    /// </summary>
    public class RAM : Component
    {
        // clock
        // latch input
        // width
        // data connections

        public NodeVector I_DATA;
        public NodeVector O_DATA;
        public NodeVector I_ENABLE;
        public NodeVector I_CLK;
        public NodeVector I_ADDRESS;
        public NodeVector I_WRITE;
        public NodeVector I_READ;

        public ClockComponent ClockIn;

        public bool RisingEdge;

        int iDataWidth;
        int iDepth;


        public RAM(Component poParentComponent, string psName, ClockComponent poClockIn, int piDataWidth, int piDepth, bool pbRisingEdge)
            : base(poParentComponent, psName)
        {
            RegisteredComponent = true;
            RisingEdge = pbRisingEdge;
            iDataWidth = piDataWidth;
            I_DATA = this.CreateNodeVector("DATA_I", iDataWidth, NodeFlowType.Sink);
            O_DATA = this.CreateNodeVector("DATA_O", iDataWidth, NodeFlowType.Source);
            I_CLK = this.CreateNodeVector("CLK_I", 1, NodeFlowType.Sink);
            I_ADDRESS = this.CreateNodeVector("ADDR_I", Conversion.MinBitWidth(piDepth), NodeFlowType.Sink);
            I_WRITE = this.CreateNodeVector("WRITE_I", 1, NodeFlowType.Sink);
            I_READ = this.CreateNodeVector("READ_I", 1, NodeFlowType.Sink);

            ClockIn = poClockIn;

            I_CLK.Connection = ClockIn.ClockOut;

            I_ENABLE = this.CreateNodeVector("ENABLE_I", 1, NodeFlowType.Sink);
            iDepth = piDepth;
        }

        public override void GenerateStructure()
        {
            //ClockIn.LinkRegister(this);
            //throw new Exception("Not Implemented yet");
        }

        public override void CalculateOutput()
        {
            // shouldn't be called for a register
        }

        public virtual void ClockEdgeEvent(bool pblnRisingEdge)
        {
            throw new Exception("Not Implemented yet");
            /*            Node nEnableNode;

                        if (pblnRisingEdge == RisingEdge)
                        {
                            SimulationDestabilizeComponent();

                            if (I_ENABLE != null)
                            {
                                nEnableNode = I_ENABLE[0];
                                if (nEnableNode.NodeState == NodeState.High)
                                    O_DATA.AssignNodeStates(I_DATA);
                                if (nEnableNode.NodeState == NodeState.Undefined)
                                    O_DATA.NodeVectorAsString = (new string('x', O_DATA.Nodes.Count));
                            }
                            else
                                O_DATA.AssignNodeStates(I_DATA);

                            SimulationStabilizeComponent();
                        }*/
        }

        public override bool InitializeSimulation()
        {
            throw new Exception("Not Implemented yet");
            /*
            SimulationDestabilizeComponent();
            SimulationStabilizeComponent();*/
            return true;
        }

        public override bool TransformStructureToVerilog()
        {
            string sVerilog = "";
            //string sOutputVector = "";
            //string sOutput = "";

            int iHighDataBit = iDataWidth - 1;
            int iHighDepth = iDepth - 1;
            ComponentVerilog.WriteVerilogText("//synthesis attribute ram_style of memory is block");
            if (iHighDataBit > 0)
                ComponentVerilog.WriteVerilogText("reg [" + iHighDataBit.ToString() + ":0] memory[0:" + iHighDepth.ToString() + "];");
            else
                ComponentVerilog.WriteVerilogText("reg " + " memory[0:" + iHighDepth.ToString() + "];");
            //ComponentVerilog.WriteVerilogText("reg " + O_DATA.Name + ";");
            int iAddressHighBit = I_ADDRESS.Width - 1;
            ComponentVerilog.WriteVerilogText("reg [" + iAddressHighBit.ToString()  + ":0] bramblock_address;");
            //ComponentVerilog.WriteVerilogText("wire [" + iAddressHighBit.ToString() + ":0] bramblock_address;");


            ComponentVerilog.WriteVerilogSkip();


            sVerilog = "always @(";
            if (RisingEdge)
                sVerilog += "posedge";
            else
                sVerilog += "negedge";
            sVerilog += " " + I_CLK.Name + ")";
            ComponentVerilog.WriteVerilogText(sVerilog);
            ComponentVerilog.WriteVerilogText("begin");
            ComponentVerilog.WriteVerilogText("bramblock_address <= " + I_ADDRESS.Name + ";",2);
            //ComponentVerilog.WriteVerilogText("if (" + I_ENABLE.Name + ")", 2);
            //ComponentVerilog.WriteVerilogText("begin", 2);

            ComponentVerilog.WriteVerilogText("if (" + I_WRITE.Name + ")", 3);
            ComponentVerilog.WriteVerilogText("begin", 3);
            //**ComponentVerilog.WriteVerilogText(O_DATA.Name + " <= " + I_DATA.Name + ";", 4);
            //ComponentVerilog.WriteVerilogText("memory[" + I_ADDRESS.Name  + "] <= " + I_DATA.Name + ";", 4);            
            ComponentVerilog.WriteVerilogText("memory[bramblock_address] <= " + I_DATA.Name + ";", 4);
            ComponentVerilog.WriteVerilogText("end", 3);
            //**ComponentVerilog.WriteVerilogText("else", 3);
            //**ComponentVerilog.WriteVerilogText("begin", 3);

            //ComponentVerilog.WriteVerilogText("if (" + I_READ.Name + ")", 4);
            //ComponentVerilog.WriteVerilogText("begin", 4);
            //ComponentVerilog.WriteVerilogText(O_DATA.Name + " <= " + "memory[" + I_ADDRESS.Name + "];", 5);
            //**ComponentVerilog.WriteVerilogText("end", 4);
            //ComponentVerilog.WriteVerilogText("else", 4);
            //ComponentVerilog.WriteVerilogText("begin", 4);
            //ComponentVerilog.WriteVerilogText(O_DATA.Name + " <= " + O_DATA.Name + ";", 5);
            //ComponentVerilog.WriteVerilogText("end", 4);

            //ComponentVerilog.WriteVerilogText("end", 3);            
            //ComponentVerilog.WriteVerilogText("end", 2);
            //ComponentVerilog.WriteVerilogText("else", 2);
            //ComponentVerilog.WriteVerilogText("begin", 2);
            //ComponentVerilog.WriteVerilogText(O_DATA.Name + " <= " + iDataWidth.ToString()  + "'b" + new String('z',iDataWidth) + ";", 4);            
            //ComponentVerilog.WriteVerilogText("end", 2);

            ComponentVerilog.WriteVerilogText("end");
            ComponentVerilog.WriteVerilogText("assign " + O_DATA.Name + " = " + "memory[bramblock_address];", 2);
            //ComponentVerilog.WriteVerilogText("assign bramblock_address = " + I_ADDRESS.Name + ";", 2);
            return true;
        }

    }
}
